package java_thinking.unit_21.Exercise.AQS;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Desc:   Condition的使用案例-生产者消费者模式
 *          https://blog.csdn.net/w_s_h_y/article/details/77450166
 *          这里我们通过一个卖烤鸭的案例来演示多生产多消费者的案例，该场景中存在两条生产线程t1和t2，用于生产烤鸭，也存在两条
 *          消费线程t3，t4用于消费烤鸭，4条线程同时执行，需要保证只有在生产线程产生烤鸭后，消费线程才能消费，否则只能等待，
 *          直到生产线程产生烤鸭后唤醒消费线程，注意烤鸭不能重复消费。ResourceByCondition类中定义product()和consume()两个
 *          方法，分别用于生产烤鸭和消费烤鸭，并且定义ReentrantLock锁，用于控制product()和consume()的并发，由于必须在烤鸭
 *          生成完成后消费线程才能消费烤鸭，否则只能等待，因此这里定义两组Condition对象，分别是producer_con和consumer_con
 *          ，前者拥有控制生产线程，后者拥有控制消费线程，这里我们使用一个标志flag来控制是否有烤鸭，当flag为true时，代表烤
 *          鸭生成完毕，生产线程必须进入等待状态同时唤醒消费线程进行消费，消费线程消费完毕后将flag设置为false，代表烤鸭消费
 *          完成，进入等待状态，同时唤醒生产线程生产烤鸭
 *
 * @author: cww
 * @DateTime: 2019-07-09 22:29
 */

public class ResourceByCondition {
    private String name;
    private int count = 1;
    /**
     * 当flag为true时，代表烤鸭生成完毕
     */
    private boolean flag = false;
    Lock lock = new ReentrantLock();
    Lock lock1 = new ReentrantLock();
    /**
     * 通过已有的锁获取两组监视器，一组监视生产者，一组监视消费者
     */
    Condition producer_con = lock.newCondition();
    Condition consumer_con = lock.newCondition();

    public void product(String name) {
        lock.lock();
        try {
            try {
                while (flag) {
                    try {
                        //如果当前已经有烤鸭生产完则挂起生产者
                        System.out.println("producer wait");
                        producer_con.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                this.name = name + count;
                count++;
                System.out.println(Thread.currentThread().getName() + "-生产者-" + this.name);
                flag = true;
                //烤鸭生产完成 直接唤醒消费线程
                consumer_con.signal();
            } catch (Exception e) {
                System.out.println("生产者报错" + e.getMessage());
            }
        } finally {
            System.out.println("product_unlock");
            lock.unlock();
        }

    }

    public void consumer() {
        lock.lock();
        try {
            try {
                while (!flag) {
                    try {
                        System.out.println("consumer wait");
                        consumer_con.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread().getName() + "-消费者-" + this.name);
                flag = false;
                //消费完毕，唤醒生产者
                producer_con.signal();
            } catch (Exception e) {
                System.out.println("消费者报错" + e.getMessage());
            }
        } finally {
            System.out.println("consumer_unlock");
            lock.unlock();

        }

    }

    public static void main(String[] args) throws InterruptedException {
        ResourceByCondition condition = new ResourceByCondition();
        Mutil_Producer producer = new Mutil_Producer(condition);
        Mutil_Consumer consumer = new Mutil_Consumer(condition);
        //两个生产者线程
        Thread t0 = new Thread(producer);
        Thread t1 = new Thread(producer);
        //两个消费者线程
        Thread t2 = new Thread(consumer);
        Thread t3 = new Thread(consumer);
        //启动线程
        t0.start();t1.start();
        t2.start();t3.start();
    }

}

/**
 * 生产者线程
 */
class Mutil_Producer implements Runnable {
    private ResourceByCondition r;

    public Mutil_Producer(ResourceByCondition r) {
        this.r = r;
    }

    @Override
    public void run() {
        while (true) {
            r.product("bei jin dark");
        }
    }
}

/**
 * 消费者线程
 */
class Mutil_Consumer implements Runnable {
    private ResourceByCondition r;

    public Mutil_Consumer(ResourceByCondition r) {
        this.r = r;
    }

    @Override
    public void run() {
        while (true) {
            r.consumer();
        }
    }
}